<?php
/**
 * basic_filterx
 *
 * @author alex_ez
 */
abstract class basic_filterx extends basic_formx {
    
	protected $field_template = "<div:divatts>\n:caption\n:before:element\n:inline-error\n:after\n</div>:extra\n\n";
	protected $fields = array();
	protected $buttons = array();
	protected $default_classes = array(
		'text' => 'textbox',
		'select' => 'select',
		'radio' => 'radio',
		'checkbox' => 'checkbox',

		'reset' => 'button',
		'submit' => 'button',
		'button' => 'button',

		'_div' => 'form-field column',
		
		'date' => 'textbox datebox',
		'number' => 'textbox short',
	);

	protected $allowed_types = array(
		'text', 'number', 'select', 'radio', 'checkbox', 'date', 'date_period', 'number_period'
		);

	protected $nonce_action = 'filter';
	protected $url = '';
	protected $form_prefix = 'filter';
	protected $form_method = 'get';
	protected $multipart = false;

	protected $options = array( );

	/**
	 * limited
	 * default value, list, and key
	 */
	private $limit = 1; // default value
	private $limits = array( 0, 20, 50, 200, 1000 );
	private $limitkey = '__limit';



	/**
	 * Display for filter form
	 */
	public function display($submit_text = false, $atts = array()) {

		$this ->buttons[] =
			array(
				'type' => 'submit',
				'class' => 'inline',
				'element_class' => 'button button-apply',
				'element_name' => null,
				'value' => __('Apply')
			);
		$this ->buttons[] =
			array(
				'type' => 'reset',
				'class' => 'inline',
				'element_class' => 'button button-reset',
				'element_name' => null,
				'value' => __('Reset')
				);
		$this ->buttons[] =
			array(
				'type' => 'button',
				'class' => 'inline',
				'element_class' => 'button button-clear',
				'element_name' => null,
				'element_onclick' => "javascript:window.location='./';",
				'value' => __('Clear'),
				'extra' => ':clear',
			);

		$r = parent::display(false, $atts);

		return $r;
	}
	
	public function set_fields($fields) {

		$r = parent::set_fields($fields);

		if (@$this->options['mode']['limited'])
		$this ->fields[$this->limitkey] = array(
			'type' => 'select',
			'element_class' => 'select',
			'label' => __('Quantity'),
			'list' => array_filter( $this ->limits )
			);

		return $r;
	}
	
	public function set_fieldset($json) {

		$r = parent::set_fieldset($json);

		foreach ($this->fields as $k => $v)
			if (!in_array($v['type'],$this->allowed_types))
				unset($this->fields[$k]);

		if (@$this->options['mode']['limited'])
		$this ->fields[$this->limitkey] = array(
			'type' => 'select',
			'element_class' => 'select',
			'label' => __('Quantity'),
			'list' => array_filter( $this ->limits )
			);

		return $r;
	}

	public function bind_data($name, $data) {
		
		if( is_array( $data ) && !isset( $data[0] ) )
			$data = array('—')+$data;
		
		return parent::bind_data( $name, $data );
	}

	public function get_postdata( $k = null ) {

		$r = parent::get_postdata( $k );
		
		if( is_array($r) && isset($r[$this->limitkey]) )
			unset( $r[$this->limitkey] );
		
		return $r;
	}


	public function get_where( )
	{
		$data = array( );
		$fs = _a::simplify( $this ->fields(), 1 ); // raw fields array
		//$q = @$this->options['mode']['strictwhere']?'':'~'; // qualifier
		$q = '~';

		//var_dump( $fs ); echo '<hr/>';
		
		//var_dump( $this ->get_postdata( ) );
		//var_dump( _a::simplify( $this ->get_postdata( ) ) );
		$postdata = $this->get_postdata() + $this->default_values();
//		$postdata = array_filter( _a::simplify( $postdata ), 'strlen' );
//var_dump($postdata, $fs);

		// prepare array to compile
		$data = array( );
		foreach( $postdata as $k => $v )
		{
			if (is_array($v))
				$v = array_filter( array_simplify( $v ), 'strlen' );

			if (is_array($v)&&empty($v) || is_string($v)&&!strlen($v))
				continue;

			$data[$k] = $v;
			/*var_dump($v);
			$field_key = array_traverse_find( $k, $fs );
			$field = array_traverse( $field_key, $fs );

			$field_subkey = substr( $k, strlen( $field_key ) );
			$field_subkey = explode( '[', str_replace( ']', '', $field_subkey ) );
			$field_subkey = array_filter( array_map( 'trim', $field_subkey ) );
			$field_subkey = array_shift( $field_subkey ) . (empty($field_subkey)?'':('[' . implode( '][', $field_subkey ) . ']'));
			var_dump(compact('k', 'field_key', 'field_subkey'));
			
//			if ($field_subkey) {
//				var_dump($field_subkey, $field_key, $data[$field_key], $v );die;
//				array_traverse_set( $field_subkey, $data[$field_key], $v );
//			}
			//array_traverse_set( $k );/
			//$data[$field_key] = $v;*/
		}
		//echo '<hr/><hr/>'; var_dump( $data ); echo '<hr/><hr/>';

		// dates
		$dtf = array( 'datetime' => 'Y-m-d H:i:s', 'date' => 'Y-m-d', 'time' => 'H:i:s', 'month' => 'Y-m-01' );
		$min = array( 'datetime' => '0000-00-00 00:00:00', 'date' => '0000-00-00 00:00:00', 'time' => '00:00:00', 'month' => '0000-00-00' );
		$max = array( 'datetime' => '9999-12-31 23:59:59', 'date' => '9999-12-31 23:59:59', 'time' => '23:59:59', 'month' => '9999-12-31' );
		
		// go on!
		$r = array( );
		foreach( $data as $k => $v )
		if( isset($v) && !is_null( $v ) && $v !== "" )
		{
			$field =& $fs[$k];
//var_dump($fs, $k);
			if( empty($field) )
				continue;
			
			$operator = null;
			
			$type = $field['type'];
			switch( $type )
			{
			case 'list':
			case 'select':
			case 'radio':
				if( !isset( $field['list'] ) )
					break;

				if( $v!==0 && empty($v) )
					continue 2;

				$list = $field['list'];
				if( is_string( $list ) )
					$list = $this->get_data($list);

				if ( is_scalar($v) && (!isset($list[$v]) || $list[$v] === '—' || $list[$v] === @$field['null']) ) // mmm...
				{
					$found = false;
					if (is_array($list))
						foreach ($list as $sublist)
							if (isset($sublist[$v]) && $sublist[$v] != '—')
								$found = true;
					if (!$found)
						continue 2;
				}

				if (!empty($field['where'])) {
					if (is_scalar($v) && !empty($field['where'][$v])) {
						if (is_array($field['where'][$v])) {
							foreach ($field['where'][$v] as $wk => $wv)
								$r[str_replace('$',$k,$wk)] = $wv;
						} else
							$r[str_replace('$',$k,$wk)] = $field['where'][$v];
					}
					continue 2;
				}

				$operator = '';

				//var_dump( $field, $list, $this->data, $k, $v );
				//die;
				//if( !isset( $fs[$k]['values'][$v] ) ) continue 2;
				// need binds before checking.
				break;

			case 'checkbox':
				if (!isset($field['list']))
					break;

				if ($v!==0 && empty($v))
					continue 2;

				$list = $field['list'];
				if (is_string($list))
					$list = $this->get_data($list);
				//var_dump($list,$v);

				if (!is_array($v))
					$v = array($v);
				
				foreach ($v as $vkkey => $vk)
				if (!isset($list[$vk])) { // mmm...
					$found = false;
					if (is_array($list))
						foreach ($list as $sublist)
							if (isset($sublist[$v]) && $sublist[$v] != '—')
								$found = true;
					if (!$found)
						unset($v[$vkkey]);
				}

				if (empty($v))
					continue 2;

				$operator = '';
				break;

			case 'date':
			case 'time':
			case 'datetime':
			case 'month':
				// @ or from+to
				//if( !@$fs[$k]['period'] ) {
				$v = @date_format( date_create( $v ), $dtf[$type] ); if( $v===false ) continue 2;
				/*} else {
				$vf = mb_strlen($v['from']) ? @date_format( date_create( $v['from'] ), $dtf[$type] ) : false;
				$vt = mb_strlen($v['to']) ? @date_format( date_create( $v['to'] ), $dtf[$type] ) : false;
				if( $vf===false && $vt === false ) continue 2;
				$v = array( $vf===false ? $min[$type] : $vf, $vt===false ? $max[$type] : $vt ); $operator = '^';
				}*/
				break;

			case 'date_period':
			case 'time_period':
			case 'datetime_period':
			case 'month_period':
				$type = str_replace( '_period', '', $type );
				
				$vf = mb_strlen(@$v['from']) ? (@date_format( date_create( $v['from'] ), $dtf[$type] ) . ' 00:00:00') : false;
				$vt = mb_strlen(@$v['to']) ? (@date_format( date_create( $v['to'] ), $dtf[$type] ) . ' 23:59:59') : false;
				
				if( $vf === false && $vt === false ) continue 2;
				$v = array(
					$vf===false ? $min[$type] : $vf,
					$vt===false ? $max[$type] : $vt );
				$operator = '^';
				break;
			case 'number_period':
				$vf = (float)(@$v['from']);
				$vt = (float)(@$v['to']);
				if(!$vf) {
					$v = $vt;
					$operator = '<';
				} elseif(!$vt) {
					$v = $vf;
					$operator = '>';
				} elseif($vf&&$vt) {
					$v = array( $vf, $vt );
					$operator = '^';
				} else {
					$v = '';
					$operator = '~';
				}
				break;
			case 'number':
				$v = array_map('intval',explode(',',$v));
				break;
			} // endswitch;
			
			$k .= pick( $operator, ($type!='number'&&$type!='list'&&$type!='radio') ? $q : '' );
			$r[$k] = $v;
		}
		//var_dump($r);die;
		bc::log($r,'filter by');
		return $r;
	}




	public function get_query( $addon = null, $remove_empty = false )
	{
		$fid = $this->form_prefix;
		$data = array();

		if (!empty($this->nonce_action)) {
			$data['action'] = $this->nonce_action;
			$data['_nonce'] = nonce($this->nonce_action);
		}

		$data[$fid] = array();
		if (@$this->options['mode']['limited'])
			$data[$fid][$this->limitkey] = $this->get_limit_id();

		$fdata = $this->_prepare_query_data( $this->get_postdata() );
		if ($remove_empty)
			$fdata = array_filter_recursive($fdata);

		$data[$fid] += (array)($fdata);
		$data += (array)($addon);

		return http_build_query( $data, 'flags' );
	}

	public function _prepare_query_data($A) {

		$r = array();
		foreach (array_keys($A) as $k) {
			$v = $A[$k];

			if( is_null($v) || $v === "" )
				continue;

			if (is_array($v)) {
				if (empty($v))
					continue;

				$v = $this->_prepare_query_data($v);
			}

			array_traverse_set($k, $r, $v);
		}

		return $r;
	}

	public function get_limit_id( )
	{
		if (!$this->options['mode']['limited'])
			return null;

		$r = $this ->get_postdata( $this ->limitkey );
		//var_dump( $r );

		if( !isset( $this ->limits[ $r ] ) ) $r = $this ->limit;
		if( !isset( $this ->limits[ $r ] ) ) $r = 1;

		return $r;
	}

	public function get_limit_value( ) {

		if (!@$this->options['mode']['limited'])
			return null;

		return (int)($this->limits[$this->get_limit_id()]);
	}

	public function get_limit( $page = 1, $count = 5000 ) {

		if (!@$this->options['mode']['limited'])
			return null;

		$page = min(max((int)$page,1), $count);
		$quantity = $this->get_limit_value();

		return array(($page-1)*$quantity, $quantity);
	}



	/**
	 * Options...
	 */
	

	/**
	 * set option
	 *
	 * @param string $k
	 * @param mixed $v
	 * @return this
	 */
	public function set_option( $k, $v )
	{
		$k = mb_strtolower( $k );
		if( $k == 'mode' )
		{
			if( !is_string( $v ) ) return;
			$modes = array_map( 'trim', array_map( 'strtolower', explode(',',$v) ) );

			foreach( array_filter( $modes ) as $vk )
				$this ->options[ $k ][ ltrim( $vk, ' -!' ) ] = (strpos( '-!', $vk[0] ) === false);
		}
		elseif( $k == 'method' )
			$this ->options[ $k ] = strtolower( $v );
		elseif( $k == 'limits' )
		{
			$this ->limits = array_merge( array(0), array_unique( array_filter( array_map( 'intval', explode( ',', ','.$v ) ) ) ) );
		}
		else
			$this ->options[ $k ] = $v;
		
		return $this;
	}

	/**
	 * set options array
	 *
	 * @param array $a
	 * @return this
	 */
	public function set_options( $a )
	{
		if( !is_array( $a ) ) return;

		foreach( $a as $k => $v )
			$this ->set_option( $k, $v );
		
		return $this;
	}

	/**
	 * get options
	 *
	 * @param string $mode
	 * @return mixed
	 */
	public function options( $mode = null )
	{
		switch( $mode )
		{
		case 'json':
			return json::encode( $this ->options );
		case 'serialize':
			return serialize( $this ->options );
		}
		
		return $this ->options;
	}





	/**
	 * Back-compatibility
	 */
	public function getOptions( )
	{
		return $this ->options( 'array' );
	}





	/**
	 * Custom field types
	 */
	protected function render_date_period($field, $input_atts) {
		$r = '';
		$id = $input_atts['id'];
		$name = $input_atts['name'];
		$value = (array)(@$input_atts['value']);
		
		$input_atts['type'] = 'text';
		$input_atts['class'] = $this ->default_class('date');
		
		$input_atts['id'] = $id . '_from';
		$input_atts['name'] = $name . '[from]';
		$input_atts['value'] = @$value['from'];
		$r .= $this->_render_input($field, $input_atts);
		
		$input_atts['id'] = $id . '_to';
		$input_atts['name'] = $name . '[to]';
		$input_atts['value'] = @$value['to'];
		$r .= $this->_render_input($field, $input_atts);

		return $r;
	}

	protected function render_number_period($field, $input_atts) {
		$r = '';
		$id = $input_atts['id'];
		$name = $input_atts['name'];
		$value = @$input_atts['value'];
		
		$input_atts['type'] = 'text';
		$input_atts['class'] = $this ->default_class('number');
		
		$input_atts['id'] = $id . '_from';
		$input_atts['name'] = $name . '[from]';
		$input_atts['value'] = @$value['from'];
		$r .= $this->_render_input($field, $input_atts);
		
		$input_atts['id'] = $id . '_to';
		$input_atts['name'] = $name . '[to]';
		$input_atts['value'] = @$value['to'];
		$r .= $this->_render_input($field, $input_atts);

		return $r;
	}


	/**
	 * Custom field types
	 */
//	protected function prepare_number_period($options, $field, $fullpath, &$raw) {
//		return array_traverse($field, $raw, false);
//	}

	protected function render_number($field, $input_atts) {
		$input_atts['type'] = 'text';
		return $this->_render_input($field, $input_atts);
	}

	protected function render_date($field, $input_atts) {
		$input_atts['type'] = 'text';
		return $this->_render_input($field, $input_atts);
	}

	protected function render_checkbox($field, $input_atts) {
		if (!empty($input_atts['list'][0]) && $input_atts['list'][0] == '—')
			unset($input_atts['list'][0]);
		return parent::render_checkbox($field, $input_atts);
	}

	protected function render_order($field, $input_atts) {
		if (!empty($input_atts['list'][0]) && $input_atts['list'][0] == '—')
			unset($input_atts['list'][0]);
		return parent::render_checkbox($field, $input_atts);
	}
}

